home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / sharpen.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-12  |  26.4 KB  |  1,093 lines

  1. /*
  2.  * "$Id: sharpen.c,v 1.23.2.1 2001/01/12 13:15:10 neo Exp $"
  3.  *
  4.  *   Sharpen filters for The GIMP -- an image manipulation program
  5.  *
  6.  *   Copyright 1997-1998 Michael Sweet (mike@easysw.com)
  7.  *
  8.  *   This program is free software; you can redistribute it and/or modify
  9.  *   it under the terms of the GNU General Public License as published by
  10.  *   the Free Software Foundation; either version 2 of the License, or
  11.  *   (at your option) any later version.
  12.  *
  13.  *   This program is distributed in the hope that it will be useful,
  14.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  *   GNU General Public License for more details.
  17.  *
  18.  *   You should have received a copy of the GNU General Public License
  19.  *   along with this program; if not, write to the Free Software
  20.  *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21.  *
  22.  * Contents:
  23.  *
  24.  *   main()                    - Main entry - just call gimp_main()...
  25.  *   query()                   - Respond to a plug-in query...
  26.  *   run()                     - Run the filter...
  27.  *   sharpen()                 - Sharpen an image using a median filter.
  28.  *   sharpen_dialog()          - Popup a dialog window for the filter box size...
  29.  *   preview_init()            - Initialize the preview window...
  30.  *   preview_scroll_callback() - Update the preview when a scrollbar is moved.
  31.  *   preview_update()          - Update the preview window.
  32.  *   preview_exit()            - Free all memory used by the preview window...
  33.  *   dialog_iscale_update()    - Update the value field using the scale.
  34.  *   dialog_ok_callback()      - Start the filter...
  35.  *   gray_filter()             - Sharpen grayscale pixels.
  36.  *   graya_filter()            - Sharpen grayscale+alpha pixels.
  37.  *   rgb_filter()              - Sharpen RGB pixels.
  38.  *   rgba_filter()             - Sharpen RGBA pixels.
  39.  *
  40.  * Revision History:
  41.  *
  42.  *   See ChangeLog
  43.  */
  44.  
  45. #include "config.h"
  46.  
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50. #include <signal.h>
  51.  
  52. #include <gtk/gtk.h>
  53.  
  54. #include <libgimp/gimp.h>
  55. #include <libgimp/gimpui.h>
  56.  
  57. #include "libgimp/stdplugins-intl.h"
  58.  
  59.  
  60. /*
  61.  * Constants...
  62.  */
  63.  
  64. #define PLUG_IN_NAME        "plug_in_sharpen"
  65. #define PLUG_IN_VERSION        "1.4.2 - 3 June 1998"
  66. #define PREVIEW_SIZE        128
  67. #define SCALE_WIDTH        100
  68.  
  69. /*
  70.  * Local functions...
  71.  */
  72.  
  73. static void    query (void);
  74. static void    run   (gchar   *name,
  75.                gint     nparams,
  76.                GimpParam  *param,
  77.                gint    *nreturn_vals,
  78.                GimpParam **returm_vals);
  79.  
  80. static void    compute_luts   (void);
  81. static void    sharpen        (void);
  82.  
  83. static gint    sharpen_dialog (void);
  84.  
  85. static void    dialog_iscale_update (GtkAdjustment *, gint *);
  86. static void    dialog_ok_callback   (GtkWidget *, gpointer);
  87.  
  88. static void    preview_init            (void);
  89. static void    preview_exit            (void);
  90. static void    preview_update          (void);
  91. static void    preview_scroll_callback (void);
  92.  
  93. typedef gint32 intneg;
  94. typedef gint32 intpos;
  95.  
  96. static void    gray_filter  (int width, guchar *src, guchar *dst, intneg *neg0,
  97.                   intneg *neg1, intneg *neg2);
  98. static void    graya_filter (int width, guchar *src, guchar *dst, intneg *neg0,
  99.                   intneg *neg1, intneg *neg2);
  100. static void    rgb_filter   (int width, guchar *src, guchar *dst, intneg *neg0,
  101.                   intneg *neg1, intneg *neg2);
  102. static void    rgba_filter  (int width, guchar *src, guchar *dst, intneg *neg0,
  103.                   intneg *neg1, intneg *neg2);
  104.  
  105.  
  106. /*
  107.  * Globals...
  108.  */
  109.  
  110. GimpPlugInInfo PLUG_IN_INFO =
  111. {
  112.   NULL,  /* init_proc  */
  113.   NULL,  /* quit_proc  */
  114.   query, /* query_proc */
  115.   run    /* run_proc   */
  116. };
  117.  
  118. static GtkWidget *preview;        /* Preview widget */
  119. static gint       preview_width;    /* Width of preview widget */
  120. static gint       preview_height;    /* Height of preview widget */
  121. static gint       preview_x1;        /* Upper-left X of preview */
  122. static gint       preview_y1;        /* Upper-left Y of preview */
  123. static gint       preview_x2;        /* Lower-right X of preview */
  124. static gint       preview_y2;        /* Lower-right Y of preview */
  125. static guchar    *preview_src;        /* Source pixel image */
  126. static intneg    *preview_neg;        /* Negative coefficient pixels */
  127. static guchar    *preview_dst;        /* Destination pixel image */
  128. static guchar    *preview_image;    /* Preview RGB image */
  129. static GtkObject *hscroll_data;        /* Horizontal scrollbar data */
  130. static GtkObject *vscroll_data;        /* Vertical scrollbar data */
  131.  
  132. static GimpDrawable *drawable = NULL;    /* Current image */
  133. static gint       sel_x1;              /* Selection bounds */
  134. static gint       sel_y1;
  135. static gint       sel_x2;
  136. static gint       sel_y2;
  137. static gint       sel_width;        /* Selection width */
  138. static gint       sel_height;        /* Selection height */
  139. static gint       img_bpp;        /* Bytes-per-pixel in image */
  140. static gint       sharpen_percent = 10;    /* Percent of sharpening */
  141. static gint       run_filter = FALSE;    /* True if we should run the filter */
  142.  
  143. static intneg     neg_lut[256];        /* Negative coefficient LUT */
  144. static intpos     pos_lut[256];        /* Positive coefficient LUT */
  145.  
  146.  
  147. MAIN ()
  148.  
  149. static void
  150. query (void)
  151. {
  152.   static GimpParamDef    args[] =
  153.   {
  154.     { GIMP_PDB_INT32,    "run_mode",    "Interactive, non-interactive" },
  155.     { GIMP_PDB_IMAGE,    "image",    "Input image" },
  156.     { GIMP_PDB_DRAWABLE,    "drawable",    "Input drawable" },
  157.     { GIMP_PDB_INT32,    "percent",    "Percent sharpening (default = 10)" }
  158.   };
  159.   static gint nargs = sizeof (args) / sizeof (args[0]);
  160.  
  161.   gimp_install_procedure (PLUG_IN_NAME,
  162.               "Sharpen filter, typically used to 'sharpen' a photographic image.",
  163.               "This plug-in selectively performs a convolution filter on an image.",
  164.               "Michael Sweet <mike@easysw.com>",
  165.               "Copyright 1997-1998 by Michael Sweet",
  166.               PLUG_IN_VERSION,
  167.               N_("<Image>/Filters/Enhance/Sharpen..."),
  168.               "RGB*, GRAY*",
  169.               GIMP_PLUGIN,
  170.               nargs, 0,
  171.               args, NULL);
  172. }
  173.  
  174. static void
  175. run (gchar   *name,
  176.      gint     nparams,
  177.      GimpParam  *param,
  178.      gint    *nreturn_vals,
  179.      GimpParam **return_vals)
  180. {
  181.   GimpRunModeType    run_mode;    /* Current run mode */
  182.   GimpPDBStatusType    status;        /* Return status */
  183.   GimpParam    *values;    /* Return values */
  184.  
  185.   /*
  186.    * Initialize parameter data...
  187.    */
  188.  
  189.   status   = GIMP_PDB_SUCCESS;
  190.   run_mode = param[0].data.d_int32;
  191.  
  192.   values = g_new (GimpParam, 1);
  193.  
  194.   values[0].type          = GIMP_PDB_STATUS;
  195.   values[0].data.d_status = status;
  196.  
  197.   *nreturn_vals = 1;
  198.   *return_vals  = values;
  199.  
  200.   /*
  201.    * Get drawable information...
  202.    */
  203.  
  204.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  205.  
  206.   gimp_drawable_mask_bounds (drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  207.  
  208.   sel_width     = sel_x2 - sel_x1;
  209.   sel_height    = sel_y2 - sel_y1;
  210.   img_bpp       = gimp_drawable_bpp (drawable->id);
  211.  
  212.   /*
  213.    * See how we will run
  214.    */
  215.  
  216.   switch (run_mode)
  217.     {
  218.     case GIMP_RUN_INTERACTIVE:
  219.       INIT_I18N_UI();
  220.       /*
  221.        * Possibly retrieve data...
  222.        */
  223.       gimp_get_data (PLUG_IN_NAME, &sharpen_percent);
  224.  
  225.       /*
  226.        * Get information from the dialog...
  227.        */
  228.       if (!sharpen_dialog ())
  229.     return;
  230.       break;
  231.  
  232.     case GIMP_RUN_NONINTERACTIVE:
  233.       INIT_I18N();
  234.       /*
  235.        * Make sure all the arguments are present...
  236.        */
  237.       if (nparams != 4)
  238.     status = GIMP_PDB_CALLING_ERROR;
  239.       else
  240.     sharpen_percent = param[3].data.d_int32;
  241.       break;
  242.  
  243.     case GIMP_RUN_WITH_LAST_VALS:
  244.       INIT_I18N();
  245.       /*
  246.        * Possibly retrieve data...
  247.        */
  248.       gimp_get_data (PLUG_IN_NAME, &sharpen_percent);
  249.       break;
  250.  
  251.     default:
  252.       status = GIMP_PDB_CALLING_ERROR;
  253.       break;;
  254.     };
  255.  
  256.   /*
  257.    * Sharpen the image...
  258.    */
  259.  
  260.   if (status == GIMP_PDB_SUCCESS)
  261.     {
  262.       if ((gimp_drawable_is_rgb (drawable->id) ||
  263.        gimp_drawable_is_gray (drawable->id)))
  264.     {
  265.       /*
  266.        * Set the tile cache size...
  267.        */
  268.       gimp_tile_cache_ntiles (2 * (drawable->width + gimp_tile_width() - 1) /
  269.                   gimp_tile_width() + 1);
  270.  
  271.       /*
  272.        * Run!
  273.        */
  274.       sharpen ();
  275.  
  276.       /*
  277.        * If run mode is interactive, flush displays...
  278.        */
  279.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  280.         gimp_displays_flush ();
  281.  
  282.       /*
  283.        * Store data...
  284.        */
  285.       if (run_mode == GIMP_RUN_INTERACTIVE)
  286.         gimp_set_data (PLUG_IN_NAME,
  287.                &sharpen_percent, sizeof (sharpen_percent));
  288.     }
  289.       else
  290.     status = GIMP_PDB_EXECUTION_ERROR;
  291.     };
  292.  
  293.   /*
  294.    * Reset the current run status...
  295.    */
  296.   values[0].data.d_status = status;
  297.  
  298.   /*
  299.    * Detach from the drawable...
  300.    */
  301.   gimp_drawable_detach (drawable);
  302. }
  303.  
  304.  
  305. static void
  306. compute_luts (void)
  307. {
  308.   gint i;    /* Looping var */
  309.   gint fact;    /* 1 - sharpness */
  310.  
  311.   fact = 100 - sharpen_percent;
  312.   if (fact < 1)
  313.     fact = 1;
  314.  
  315.   for (i = 0; i < 256; i ++)
  316.     {
  317.       pos_lut[i] = 800 * i / fact;
  318.       neg_lut[i] = (4 + pos_lut[i] - (i << 3)) >> 3;
  319.     };
  320. }
  321.  
  322. /*
  323.  * 'sharpen()' - Sharpen an image using a convolution filter.
  324.  */
  325.  
  326. static void
  327. sharpen (void)
  328. {
  329.   GimpPixelRgn    src_rgn,    /* Source image region */
  330.         dst_rgn;    /* Destination image region */
  331.   guchar    *src_rows[4],    /* Source pixel rows */
  332.         *src_ptr,    /* Current source pixel */
  333.         *dst_row;    /* Destination pixel row */
  334.   intneg    *neg_rows[4],    /* Negative coefficient rows */
  335.         *neg_ptr;    /* Current negative coefficient */
  336.   gint        i,        /* Looping vars */
  337.         y,        /* Current location in image */
  338.         row,        /* Current row in src_rows */
  339.         count,        /* Current number of filled src_rows */
  340.         width;        /* Byte width of the image */
  341.   void        (*filter)(int, guchar *, guchar *, intneg *, intneg *, intneg *);
  342.  
  343.   filter = NULL;
  344.  
  345.   /*
  346.    * Let the user know what we're doing...
  347.    */
  348.   gimp_progress_init( _("Sharpening..."));
  349.  
  350.   /*
  351.    * Setup for filter...
  352.    */
  353.  
  354.   gimp_pixel_rgn_init (&src_rgn, drawable,
  355.                sel_x1, sel_y1, sel_width, sel_height, FALSE, FALSE);
  356.   gimp_pixel_rgn_init (&dst_rgn, drawable,
  357.                sel_x1, sel_y1, sel_width, sel_height, TRUE, TRUE);
  358.  
  359.   compute_luts ();
  360.  
  361.   width = sel_width * img_bpp;
  362.  
  363.   for (row = 0; row < 4; row ++)
  364.     {
  365.       src_rows[row] = g_new (guchar, width);
  366.       neg_rows[row] = g_new (intneg, width);
  367.     };
  368.  
  369.   dst_row = g_new (guchar, width);
  370.  
  371.   /*
  372.    * Pre-load the first row for the filter...
  373.    */
  374.  
  375.   gimp_pixel_rgn_get_row (&src_rgn, src_rows[0], sel_x1, sel_y1, sel_width);
  376.   for (i = width, src_ptr = src_rows[0], neg_ptr = neg_rows[0];
  377.        i > 0;
  378.        i --, src_ptr ++, neg_ptr ++)
  379.     *neg_ptr = neg_lut[*src_ptr];
  380.  
  381.   row   = 1;
  382.   count = 1;
  383.  
  384.   /*
  385.    * Select the filter...
  386.    */
  387.  
  388.   switch (img_bpp)
  389.     {
  390.     case 1 :
  391.       filter = gray_filter;
  392.       break;
  393.     case 2 :
  394.       filter = graya_filter;
  395.       break;
  396.     case 3 :
  397.       filter = rgb_filter;
  398.       break;
  399.     case 4 :
  400.       filter = rgba_filter;
  401.       break;
  402.     };
  403.  
  404.   /*
  405.    * Sharpen...
  406.    */
  407.  
  408.   for (y = sel_y1; y < sel_y2; y ++)
  409.     {
  410.       /*
  411.        * Load the next pixel row...
  412.        */
  413.  
  414.       if ((y + 1) < sel_y2)
  415.     {
  416.       /*
  417.        * Check to see if our src_rows[] array is overflowing yet...
  418.        */
  419.  
  420.       if (count >= 3)
  421.         count --;
  422.  
  423.       /*
  424.        * Grab the next row...
  425.        */
  426.  
  427.       gimp_pixel_rgn_get_row (&src_rgn, src_rows[row],
  428.                   sel_x1, y + 1, sel_width);
  429.       for (i = width, src_ptr = src_rows[row], neg_ptr = neg_rows[row];
  430.            i > 0;
  431.            i --, src_ptr ++, neg_ptr ++)
  432.         *neg_ptr = neg_lut[*src_ptr];
  433.  
  434.       count ++;
  435.       row = (row + 1) & 3;
  436.     }
  437.       else
  438.     {
  439.       /*
  440.        * No more pixels at the bottom...  Drop the oldest samples...
  441.        */
  442.  
  443.       count --;
  444.     };
  445.  
  446.       /*
  447.        * Now sharpen pixels and save the results...
  448.        */
  449.  
  450.       if (count == 3)
  451.     {
  452.       (* filter) (sel_width, src_rows[(row + 2) & 3], dst_row,
  453.               neg_rows[(row + 1) & 3] + img_bpp,
  454.               neg_rows[(row + 2) & 3] + img_bpp,
  455.               neg_rows[(row + 3) & 3] + img_bpp);
  456.  
  457.       /*
  458.        * Set the row...
  459.        */
  460.  
  461.       gimp_pixel_rgn_set_row (&dst_rgn, dst_row, sel_x1, y, sel_width);
  462.     }
  463.       else if (count == 2)
  464.     {
  465.       if (y == sel_y1)    /* first row */
  466.         gimp_pixel_rgn_set_row (&dst_rgn, src_rows[0], 
  467.                     sel_x1, y, sel_width);
  468.       else                  /* last row  */
  469.         gimp_pixel_rgn_set_row (&dst_rgn, src_rows[(sel_height - 1) & 3], 
  470.                     sel_x1, y, sel_width);
  471.     };
  472.  
  473.       if ((y & 15) == 0)
  474.     gimp_progress_update ((gdouble) (y - sel_y1) / (gdouble) sel_height);
  475.     };
  476.  
  477.   /*
  478.    * OK, we're done.  Free all memory used...
  479.    */
  480.  
  481.   for (row = 0; row < 4; row ++)
  482.     {
  483.       g_free (src_rows[row]);
  484.       g_free (neg_rows[row]);
  485.     };
  486.  
  487.   g_free (dst_row);
  488.  
  489.   /*
  490.    * Update the screen...
  491.    */
  492.  
  493.   gimp_drawable_flush (drawable);
  494.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  495.   gimp_drawable_update (drawable->id, sel_x1, sel_y1, sel_width, sel_height);
  496. }
  497.  
  498.  
  499. /*
  500.  * 'sharpen_dialog()' - Popup a dialog window for the filter box size...
  501.  */
  502.  
  503. static gint
  504. sharpen_dialog (void)
  505. {
  506.   GtkWidget *dialog;
  507.   GtkWidget *vbox;
  508.   GtkWidget *table;
  509.   GtkWidget *abox;
  510.   GtkWidget *ptable;
  511.   GtkWidget *frame;
  512.   GtkWidget *scrollbar;
  513.   GtkObject *adj;
  514.   gchar *title;
  515.  
  516.   gimp_ui_init ("sharpen", TRUE);
  517.  
  518.   title = g_strdup_printf (_("Sharpen - %s"), PLUG_IN_VERSION);
  519.  
  520.   dialog = gimp_dialog_new (title, "sharpen",
  521.                 gimp_standard_help_func, "filters/sharpen.html",
  522.                 GTK_WIN_POS_MOUSE,
  523.                 FALSE, TRUE, FALSE,
  524.  
  525.                 _("OK"), dialog_ok_callback,
  526.                 NULL, NULL, NULL, TRUE, FALSE,
  527.                 _("Cancel"), gtk_widget_destroy,
  528.                 NULL, 1, NULL, FALSE, TRUE,
  529.  
  530.                 NULL);
  531.  
  532.   g_free (title);
  533.  
  534.   gtk_signal_connect (GTK_OBJECT(dialog), "destroy",
  535.               GTK_SIGNAL_FUNC (gtk_main_quit),
  536.               NULL);
  537.  
  538.   /*
  539.    * Top-level table for dialog...
  540.    */
  541.  
  542.   vbox = gtk_vbox_new (FALSE, 4);
  543.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  544.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
  545.                       FALSE, FALSE, 0);
  546.   gtk_widget_show (vbox);
  547.  
  548.   /*
  549.    * Preview window...
  550.    */
  551.  
  552.   frame = gtk_frame_new (_("Preview"));
  553.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  554.   gtk_widget_show (frame);
  555.  
  556.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  557.   gtk_container_set_border_width (GTK_CONTAINER (abox), 4);
  558.   gtk_container_add (GTK_CONTAINER (frame), abox);
  559.   gtk_widget_show (abox);
  560.  
  561.   ptable = gtk_table_new (2, 2, FALSE);
  562.   gtk_container_add (GTK_CONTAINER (abox), ptable);
  563.   gtk_widget_show (ptable);
  564.  
  565.   frame = gtk_frame_new (NULL);
  566.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  567.   gtk_table_attach (GTK_TABLE (ptable), frame, 0, 1, 0, 1, 0, 0, 0, 0);
  568.   gtk_widget_show (frame);
  569.  
  570.   preview_width  = MIN (sel_width, PREVIEW_SIZE);
  571.   preview_height = MIN (sel_height, PREVIEW_SIZE);
  572.  
  573.   preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  574.   gtk_preview_size (GTK_PREVIEW (preview), preview_width, preview_height);
  575.   gtk_container_add (GTK_CONTAINER (frame), preview);
  576.   gtk_widget_show (preview);
  577.  
  578.   hscroll_data = gtk_adjustment_new (0, 0, sel_width - 1, 1.0,
  579.                      MIN (preview_width, sel_width),
  580.                      MIN (preview_width, sel_width));
  581.  
  582.   gtk_signal_connect (hscroll_data, "value_changed",
  583.               (GtkSignalFunc) preview_scroll_callback,
  584.               NULL);
  585.  
  586.   scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (hscroll_data));
  587.   gtk_range_set_update_policy (GTK_RANGE (scrollbar), GTK_UPDATE_CONTINUOUS);
  588.   gtk_table_attach (GTK_TABLE (ptable), scrollbar, 0, 1, 1, 2,
  589.             GTK_FILL, 0, 0, 0);
  590.   gtk_widget_show (scrollbar);
  591.  
  592.   vscroll_data = gtk_adjustment_new (0, 0, sel_height - 1, 1.0,
  593.                      MIN (preview_height, sel_height),
  594.                      MIN (preview_height, sel_height));
  595.  
  596.   gtk_signal_connect (vscroll_data, "value_changed",
  597.               GTK_SIGNAL_FUNC (preview_scroll_callback),
  598.               NULL);
  599.  
  600.   scrollbar = gtk_vscrollbar_new (GTK_ADJUSTMENT (vscroll_data));
  601.   gtk_range_set_update_policy (GTK_RANGE (scrollbar), GTK_UPDATE_CONTINUOUS);
  602.   gtk_table_attach (GTK_TABLE (ptable), scrollbar, 1, 2, 0, 1,
  603.             0, GTK_FILL, 0, 0);
  604.   gtk_widget_show (scrollbar);
  605.  
  606.   preview_init ();
  607.  
  608.   /*
  609.    * Sharpness control...
  610.    */
  611.  
  612.   frame = gtk_frame_new (_("Parameter Settings"));
  613.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  614.   gtk_widget_show (frame);
  615.  
  616.   table = gtk_table_new (1, 3, FALSE);
  617.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  618.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  619.   gtk_container_add (GTK_CONTAINER (frame), table);
  620.   gtk_widget_show (table);
  621.  
  622.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  623.                   _("Sharpness:"), SCALE_WIDTH, 0,
  624.                   sharpen_percent, 1, 99, 1, 10, 0,
  625.                   TRUE, 0, 0,
  626.                   NULL, NULL);
  627.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  628.               GTK_SIGNAL_FUNC (dialog_iscale_update),
  629.               &sharpen_percent);
  630.  
  631.   gtk_widget_show (dialog);
  632.  
  633.   preview_update ();
  634.  
  635.   gtk_main ();
  636.   gdk_flush ();
  637.  
  638.   preview_exit ();
  639.  
  640.   return run_filter;
  641. }
  642.  
  643. /*  preview functions  */
  644.  
  645. static void
  646. preview_init (void)
  647. {
  648.   gint width;  /* Byte width of the image */
  649.  
  650.   /*
  651.    * Setup for preview filter...
  652.    */
  653.  
  654.   compute_luts();
  655.  
  656.   width = preview_width * img_bpp;
  657.  
  658.   preview_src   = g_new (guchar, width * preview_height);
  659.   preview_neg   = g_new (intneg, width * preview_height);
  660.   preview_dst   = g_new (guchar, width * preview_height);
  661.   preview_image = g_new (guchar, preview_width * preview_height * 3);
  662.  
  663.   preview_x1 = sel_x1;
  664.   preview_y1 = sel_y1;
  665.   preview_x2 = preview_x1 + preview_width;
  666.   preview_y2 = preview_y1 + preview_height;
  667. }
  668.  
  669. static void
  670. preview_scroll_callback (void)
  671. {
  672.   preview_x1 = sel_x1 + GTK_ADJUSTMENT (hscroll_data)->value;
  673.   preview_y1 = sel_y1 + GTK_ADJUSTMENT (vscroll_data)->value;
  674.   preview_x2 = preview_x1 + MIN (preview_width, sel_width);
  675.   preview_y2 = preview_y1 + MIN (preview_height, sel_height);
  676.  
  677.   preview_update ();
  678. }
  679.  
  680. static void
  681. preview_update (void)
  682. {
  683.   GimpPixelRgn    src_rgn;    /* Source image region */
  684.   guchar    *src_ptr,    /* Current source pixel */
  685.         *dst_ptr,    /* Current destination pixel */
  686.           *image_ptr;    /* Current image pixel */
  687.   intneg    *neg_ptr;    /* Current negative pixel */
  688.   guchar    check;        /* Current check mark pixel */
  689.   gint        i,          /* Looping var */
  690.         x, y,        /* Current location in image */
  691.         width;        /* Byte width of the image */
  692.   void        (*filter)(int, guchar *, guchar *, intneg *, intneg *, intneg *);
  693.  
  694.   filter = NULL;
  695.  
  696.   /*
  697.    * Setup for filter...
  698.    */
  699.  
  700.   gimp_pixel_rgn_init (&src_rgn, drawable,
  701.                preview_x1, preview_y1, preview_width, preview_height,
  702.                FALSE, FALSE);
  703.  
  704.   width = preview_width * img_bpp;
  705.  
  706.   /*
  707.    * Load the preview area...
  708.    */
  709.  
  710.   gimp_pixel_rgn_get_rect (&src_rgn, preview_src, preview_x1, preview_y1,
  711.                preview_width, preview_height);
  712.  
  713.   for (i = width * preview_height, src_ptr = preview_src, neg_ptr = preview_neg;
  714.        i > 0;
  715.        i --)
  716.     *neg_ptr++ = neg_lut[*src_ptr++];
  717.  
  718.   /*
  719.    * Select the filter...
  720.    */
  721.  
  722.   switch (img_bpp)
  723.     {
  724.     case 1:
  725.       filter = gray_filter;
  726.       break;
  727.     case 2:
  728.       filter = graya_filter;
  729.       break;
  730.     case 3:
  731.       filter = rgb_filter;
  732.       break;
  733.     case 4:
  734.       filter = rgba_filter;
  735.       break;
  736.     };
  737.  
  738.   /*
  739.    * Sharpen...
  740.    */
  741.  
  742.   memcpy (preview_dst, preview_src, width);
  743.   memcpy (preview_dst + width * (preview_height - 1),
  744.       preview_src + width * (preview_height - 1),
  745.       width);
  746.  
  747.   for (y = preview_height - 2, src_ptr = preview_src + width,
  748.        neg_ptr = preview_neg + width + img_bpp,
  749.        dst_ptr = preview_dst + width;
  750.        y > 0;
  751.        y --, src_ptr += width, neg_ptr += width, dst_ptr += width)
  752.     (*filter)(preview_width, src_ptr, dst_ptr, neg_ptr - width,
  753.               neg_ptr, neg_ptr + width);
  754.  
  755.   /*
  756.    * Fill the preview image buffer...
  757.    */
  758.  
  759.   switch (img_bpp)
  760.     {
  761.     case 1:
  762.       for (x = preview_width * preview_height, dst_ptr = preview_dst,
  763.          image_ptr = preview_image;
  764.        x > 0;
  765.        x --, dst_ptr ++, image_ptr += 3)
  766.     image_ptr[0] = image_ptr[1] = image_ptr[2] = *dst_ptr;
  767.       break;
  768.  
  769.     case 2:
  770.       for (y = preview_height, dst_ptr = preview_dst,
  771.          image_ptr = preview_image;
  772.        y > 0;
  773.        y --)
  774.     for (x = preview_width;
  775.          x > 0;
  776.          x --, dst_ptr += 2, image_ptr += 3)
  777.       if (dst_ptr[1] == 255)
  778.         image_ptr[0] = image_ptr[1] = image_ptr[2] = *dst_ptr;
  779.       else
  780.         {
  781.               if ((y & GIMP_CHECK_SIZE) ^ (x & GIMP_CHECK_SIZE))
  782.                 check = GIMP_CHECK_LIGHT * 255;
  783.               else
  784.                 check = GIMP_CHECK_DARK * 255;
  785.  
  786.               if (dst_ptr[1] == 0)
  787.                 image_ptr[0] = image_ptr[1] = image_ptr[2] = check;
  788.               else
  789.                 image_ptr[0] = image_ptr[1] = image_ptr[2] =
  790.           check + ((dst_ptr[0] - check) * dst_ptr[1]) / 255;
  791.         };
  792.       break;
  793.  
  794.     case 3:
  795.       memcpy (preview_image, preview_dst, preview_width * preview_height * 3);
  796.       break;
  797.  
  798.     case 4:
  799.       for (y = preview_height, dst_ptr = preview_dst,
  800.          image_ptr = preview_image;
  801.        y > 0;
  802.        y --)
  803.     for (x = preview_width;
  804.          x > 0;
  805.          x --, dst_ptr += 4, image_ptr += 3)
  806.       if (dst_ptr[3] == 255)
  807.         {
  808.           image_ptr[0] = dst_ptr[0];
  809.           image_ptr[1] = dst_ptr[1];
  810.           image_ptr[2] = dst_ptr[2];
  811.         }
  812.       else
  813.         {
  814.               if ((y & GIMP_CHECK_SIZE) ^ (x & GIMP_CHECK_SIZE))
  815.                 check = GIMP_CHECK_LIGHT * 255;
  816.               else
  817.                 check = GIMP_CHECK_DARK * 255;
  818.  
  819.               if (dst_ptr[3] == 0)
  820.                 image_ptr[0] = image_ptr[1] = image_ptr[2] = check;
  821.               else
  822.         {
  823.           image_ptr[0] =
  824.             check + ((dst_ptr[0] - check) * dst_ptr[3]) / 255;
  825.           image_ptr[1] =
  826.             check + ((dst_ptr[1] - check) * dst_ptr[3]) / 255;
  827.           image_ptr[2] =
  828.             check + ((dst_ptr[2] - check) * dst_ptr[3]) / 255;
  829.         };
  830.         };
  831.       break;
  832.     };
  833.  
  834.   /*
  835.    * Draw the preview image on the screen...
  836.    */
  837.  
  838.   for (y = 0, image_ptr = preview_image;
  839.        y < preview_height;
  840.        y ++, image_ptr += preview_width * 3)
  841.     gtk_preview_draw_row (GTK_PREVIEW (preview), image_ptr, 0, y,
  842.               preview_width);
  843.  
  844.   gtk_widget_draw (preview, NULL);
  845.   gdk_flush ();
  846. }
  847.  
  848. static void
  849. preview_exit (void)
  850. {
  851.   g_free (preview_src);
  852.   g_free (preview_neg);
  853.   g_free (preview_dst);
  854.   g_free (preview_image);
  855. }
  856.  
  857. /*  dialog callbacks  */
  858.  
  859. static void
  860. dialog_iscale_update (GtkAdjustment *adjustment,
  861.               gint          *value)
  862. {
  863.   gimp_int_adjustment_update (adjustment, value);
  864.  
  865.   compute_luts ();
  866.   preview_update ();
  867. }
  868.  
  869. static void
  870. dialog_ok_callback (GtkWidget *widget,
  871.             gpointer   data)
  872. {
  873.   run_filter = TRUE;
  874.  
  875.   gtk_widget_destroy (GTK_WIDGET (data));
  876. }
  877.  
  878.  
  879. /*
  880.  * 'gray_filter()' - Sharpen grayscale pixels.
  881.  */
  882.  
  883. static void
  884. gray_filter (gint    width,    /* I - Width of line in pixels */
  885.          guchar *src,    /* I - Source line */
  886.          guchar *dst,    /* O - Destination line */
  887.          intneg *neg0,    /* I - Top negative coefficient line */
  888.          intneg *neg1,    /* I - Middle negative coefficient line */
  889.          intneg *neg2)    /* I - Bottom negative coefficient line */
  890. {
  891.   intpos pixel;        /* New pixel value */
  892.  
  893.   *dst++ = *src++;
  894.   width -= 2;
  895.  
  896.   while (width > 0)
  897.     {
  898.       pixel = (pos_lut[*src++] - neg0[-1] - neg0[0] - neg0[1] -
  899.            neg1[-1] - neg1[1] -
  900.            neg2[-1] - neg2[0] - neg2[1]);
  901.       pixel = (pixel + 4) >> 3;
  902.       if (pixel < 0)
  903.     *dst++ = 0;
  904.       else if (pixel < 255)
  905.     *dst++ = pixel;
  906.       else
  907.     *dst++ = 255;
  908.  
  909.       neg0 ++;
  910.       neg1 ++;
  911.       neg2 ++;
  912.       width --;
  913.     };
  914.  
  915.   *dst++ = *src++;
  916. }
  917.  
  918. /*
  919.  * 'graya_filter()' - Sharpen grayscale+alpha pixels.
  920.  */
  921.  
  922. static void
  923. graya_filter (gint   width,    /* I - Width of line in pixels */
  924.           guchar *src,    /* I - Source line */
  925.           guchar *dst,    /* O - Destination line */
  926.           intneg *neg0,    /* I - Top negative coefficient line */
  927.           intneg *neg1,    /* I - Middle negative coefficient line */
  928.           intneg *neg2)    /* I - Bottom negative coefficient line */
  929. {
  930.   intpos pixel;        /* New pixel value */
  931.  
  932.   *dst++ = *src++;
  933.   *dst++ = *src++;
  934.   width -= 2;
  935.  
  936.   while (width > 0)
  937.     {
  938.       pixel = (pos_lut[*src++] - neg0[-2] - neg0[0] - neg0[2] -
  939.            neg1[-2] - neg1[2] -
  940.            neg2[-2] - neg2[0] - neg2[2]);
  941.       pixel = (pixel + 4) >> 3;
  942.       if (pixel < 0)
  943.     *dst++ = 0;
  944.       else if (pixel < 255)
  945.     *dst++ = pixel;
  946.       else
  947.     *dst++ = 255;
  948.  
  949.       *dst++ = *src++;
  950.       neg0 += 2;
  951.       neg1 += 2;
  952.       neg2 += 2;
  953.       width --;
  954.     };
  955.  
  956.   *dst++ = *src++;
  957.   *dst++ = *src++;
  958. }
  959.  
  960. /*
  961.  * 'rgb_filter()' - Sharpen RGB pixels.
  962.  */
  963.  
  964. static void
  965. rgb_filter (gint    width,    /* I - Width of line in pixels */
  966.         guchar *src,    /* I - Source line */
  967.         guchar *dst,    /* O - Destination line */
  968.         intneg *neg0,    /* I - Top negative coefficient line */
  969.         intneg *neg1,    /* I - Middle negative coefficient line */
  970.         intneg *neg2)    /* I - Bottom negative coefficient line */
  971. {
  972.   intpos pixel;        /* New pixel value */
  973.  
  974.   *dst++ = *src++;
  975.   *dst++ = *src++;
  976.   *dst++ = *src++;
  977.   width -= 2;
  978.  
  979.   while (width > 0)
  980.     {
  981.       pixel = (pos_lut[*src++] - neg0[-3] - neg0[0] - neg0[3] -
  982.            neg1[-3] - neg1[3] -
  983.            neg2[-3] - neg2[0] - neg2[3]);
  984.       pixel = (pixel + 4) >> 3;
  985.       if (pixel < 0)
  986.     *dst++ = 0;
  987.       else if (pixel < 255)
  988.     *dst++ = pixel;
  989.       else
  990.     *dst++ = 255;
  991.  
  992.       pixel = (pos_lut[*src++] - neg0[-2] - neg0[1] - neg0[4] -
  993.            neg1[-2] - neg1[4] -
  994.            neg2[-2] - neg2[1] - neg2[4]);
  995.       pixel = (pixel + 4) >> 3;
  996.       if (pixel < 0)
  997.     *dst++ = 0;
  998.       else if (pixel < 255)
  999.     *dst++ = pixel;
  1000.       else
  1001.     *dst++ = 255;
  1002.  
  1003.       pixel = (pos_lut[*src++] - neg0[-1] - neg0[2] - neg0[5] -
  1004.            neg1[-1] - neg1[5] -
  1005.            neg2[-1] - neg2[2] - neg2[5]);
  1006.       pixel = (pixel + 4) >> 3;
  1007.       if (pixel < 0)
  1008.     *dst++ = 0;
  1009.       else if (pixel < 255)
  1010.     *dst++ = pixel;
  1011.       else
  1012.     *dst++ = 255;
  1013.  
  1014.       neg0 += 3;
  1015.       neg1 += 3;
  1016.       neg2 += 3;
  1017.       width --;
  1018.     };
  1019.  
  1020.   *dst++ = *src++;
  1021.   *dst++ = *src++;
  1022.   *dst++ = *src++;
  1023. }
  1024.  
  1025. /*
  1026.  * 'rgba_filter()' - Sharpen RGBA pixels.
  1027.  */
  1028.  
  1029. static void
  1030. rgba_filter (gint   width,    /* I - Width of line in pixels */
  1031.          guchar *src,    /* I - Source line */
  1032.          guchar *dst,    /* O - Destination line */
  1033.          intneg *neg0,    /* I - Top negative coefficient line */
  1034.          intneg *neg1,    /* I - Middle negative coefficient line */
  1035.          intneg *neg2)    /* I - Bottom negative coefficient line */
  1036. {
  1037.   intpos pixel;        /* New pixel value */
  1038.  
  1039.   *dst++ = *src++;
  1040.   *dst++ = *src++;
  1041.   *dst++ = *src++;
  1042.   *dst++ = *src++;
  1043.   width -= 2;
  1044.  
  1045.   while (width > 0)
  1046.     {
  1047.       pixel = (pos_lut[*src++] - neg0[-4] - neg0[0] - neg0[4] -
  1048.            neg1[-4] - neg1[4] -
  1049.            neg2[-4] - neg2[0] - neg2[4]);
  1050.       pixel = (pixel + 4) >> 3;
  1051.       if (pixel < 0)
  1052.     *dst++ = 0;
  1053.       else if (pixel < 255)
  1054.     *dst++ = pixel;
  1055.       else
  1056.     *dst++ = 255;
  1057.  
  1058.       pixel = (pos_lut[*src++] - neg0[-3] - neg0[1] - neg0[5] -
  1059.            neg1[-3] - neg1[5] -
  1060.            neg2[-3] - neg2[1] - neg2[5]);
  1061.       pixel = (pixel + 4) >> 3;
  1062.       if (pixel < 0)
  1063.     *dst++ = 0;
  1064.       else if (pixel < 255)
  1065.     *dst++ = pixel;
  1066.       else
  1067.     *dst++ = 255;
  1068.  
  1069.       pixel = (pos_lut[*src++] - neg0[-2] - neg0[2] - neg0[6] -
  1070.            neg1[-2] - neg1[6] -
  1071.            neg2[-2] - neg2[2] - neg2[6]);
  1072.       pixel = (pixel + 4) >> 3;
  1073.       if (pixel < 0)
  1074.     *dst++ = 0;
  1075.       else if (pixel < 255)
  1076.     *dst++ = pixel;
  1077.       else
  1078.     *dst++ = 255;
  1079.  
  1080.       *dst++ = *src++;
  1081.  
  1082.       neg0 += 4;
  1083.       neg1 += 4;
  1084.       neg2 += 4;
  1085.       width --;
  1086.     };
  1087.  
  1088.   *dst++ = *src++;
  1089.   *dst++ = *src++;
  1090.   *dst++ = *src++;
  1091.   *dst++ = *src++;
  1092. }
  1093.